// HTTP/1.1 200 OK
// Server: nginx
// Date: Sun, 05 Jan 2020 17:39:08 GMT
// Content-Type: text/html; charset=UTF-8
// Connection: close
// Vary: Accept-Encoding
// X-Powered-By: PHP/5.6.10
// Content-Length: 6
//
// 123Abc

// --> parse -->

// {
//     "reqline": "HTTP/1.1 200 OK",
//     "headers": {
//        "server": "nginx",
//         "date": "Sun, 05 Jan 2020 17:39:08 GMT",
//         "content-type": "text/html; charset=UTF-8",
//         "connection": "close",
//         "vary": "Accept-Encoding",
//         "x-powered-by": "PHP/5.6.10",
//         "content-length": "6",
//      }
//     "body": "123Abc"
// }
var httpParser = {};
(function () {

    function Stream (str) {
        this.pos = 0;
        this.str = str;
    }
    Stream.prototype.hasNext = function () {
        return this.pos < this.str.length;
    }
    Stream.prototype.next =  function () {
        return this.str[this.pos++];
    }

    const STATE = {
        BEGIN : 0,
        LINE_ALMOST_DONE : 1,
        LINE_DONE : 2,
        KEY_DONE : 3,
        HEADER_ALMOST_DONE : 4,
        HEADER_DONE : 5,
        END : 6
    };

    httpParser.state = STATE.BEGIN;

    function updateState(st){
        httpParser.state = st;
    }

    function currentState(){
        return httpParser.state;
    }

    httpParser.parse = function (str) {
        if (str.length < 4) return -1;
        updateState(STATE.BEGIN);
        var result = {};
        result["headers"] = {};
        var lastword = "";
        var word = "";
        var stream = new Stream(str);
        while (stream.hasNext()) {
            var ch = stream.next();
            switch (currentState()) {
                case STATE.BEGIN:
                    // they might be just sending \n instead of \r\n so this would be
                    // the second \n to denote the end of headers
                    if (ch == '\r' || ch == '\n') {
                        updateState(STATE.LINE_ALMOST_DONE);
                        result["reqline"] = word;
                        word = "";
                        break;
                    }
                    word += ch;
                    break;
                case STATE.LINE_ALMOST_DONE:
                    if (ch == '\n') {
                        updateState(STATE.LINE_DONE);
                    }else{
                        return -1;
                    }
                    break;
                case STATE.LINE_DONE:
                    if (ch == ':') {
                        updateState(STATE.KEY_DONE);
                        lastword = word;
                        word = "";
                        break;
                    }
                    if (ch == '\r' || ch == '\n') {
                        updateState(STATE.HEADER_ALMOST_DONE);
                        lastword = "";
                        word = "";
                        break;
                    }
                    word += ch;
                    break;
                case STATE.KEY_DONE:
                    if (ch == '\r' || ch == '\n') {
                        updateState(STATE.LINE_ALMOST_DONE);
                        result["headers"][lastword.toLowerCase()] = word;
                        lastword = "";
                        word = "";
                        break;
                    }
                    if (word == "" && ch == ' ') break;
                    word += ch;
                    break;
                case STATE.HEADER_ALMOST_DONE:
                    if (ch == '\n') {
                        updateState(STATE.HEADER_DONE);
                    }else{
                        return -1;
                    }
                    break;
                //Need to optimize
                case STATE.HEADER_DONE:
                    word += ch;
                    while (stream.hasNext()){
                        word += stream.next();
                    }
                    result["body"] = word;
                    word = "";
                    break;
                default:
                    return -2;
            }
        }
        return result;
    }

})();

var http = {};
(function () {

    var decoder = new TextDecoder();

    function Request (parseResult) {
        this.reqline = parseResult["reqline"];
        this.headers = parseResult["headers"];
        this.body = parseResult["body"];
    }

    function Response (conn) {
        this.conn = conn;
        this.headers="";
    }
    Response.prototype.writeHead = function (stateCode,headersDict) {
        stateCode = stateCode || 200;
        headersDict = headersDict || {"connection":"close"};
        this.headers = "HTTP/1.1 " + stateCode + "\r\n";
        for (var key in headersDict) {
            this.headers = this.headers + key + ': ' + headersDict[key] + "\r\n";
        }
        this.headers += "\r\n";
    }
    Response.prototype.send = function (body) {
        //this.headers = this.headers + "content-length: " + Buffer.byteLength(body, 'utf8') + "\r\n\r\n";
        this.conn.send(this.headers + body);
        this.conn.onsent = function (conn) {
            conn.close();
        }
    }

    // requestListener(req,res)
    http.createServer = function (port,requestListener) {
        net.createTcpServer(port,function (conn){
            conn.onrecv = function (conn, workBuffer, ret) {
                var req = new Request(httpParser.parse(decoder.decode(workBuffer.subarray(0,ret))));
                var res = new Response(conn);
                requestListener(req,res);
            }
        });
    }

})();
